﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Net;
using System.Windows.Forms;

namespace UpdatePro
{
    /// <summary>
    /// 升级管理器
    /// </summary>
    public class UpdateManager
    {
        public UpdateManager()
        {
            //1.初始化对象
            this.LastUpdateInfo = new UpdateInfo();
            this.NowUpdateInfo = new UpdateInfo() { FileList = new List<string[]>()};
            //2.给对象赋值：读取xml文件
            GetLastUpdateInfo();
            GetNewUpdateInfo();
        }



        /// <summary>
        /// 上次跟新信息【版本号、跟新时间、文件所在服务器地址】
        /// </summary>
        public UpdateInfo LastUpdateInfo { get; set; }

        /// <summary>
        /// 当前跟新信息【版本号、跟新时间、文件所在服务器地址】
        /// </summary>
        public UpdateInfo NowUpdateInfo { get; set; }

        /// <summary>
        /// 是否需要更新
        /// </summary>
        public bool IsUpdate
        {
            get 
            {
                DateTime lastUpdateTime = LastUpdateInfo.UpdateTime;
                DateTime NowUpdateTime = NowUpdateInfo.UpdateTime;
                return NowUpdateTime > lastUpdateTime;
            }
        }

        /// <summary>
        /// 存放下载的更新文件的临时目录
        /// </summary>
        public string TempFilePath 
        {
            get
            {
                var temppath = Environment.GetEnvironmentVariable("Temp")+"\\updateFile";
                if (!Directory.Exists(temppath))
                {
                    Directory.CreateDirectory(temppath);
                }
                return temppath;
            }
        
        }

        /// <summary>
        /// 从本地获取上次跟新的信息
        /// </summary>
        /// <returns></returns>
        private void GetLastUpdateInfo()
        {
            FileStream fileStream = new FileStream("UpdateList.xml", FileMode.Open);
            XmlTextReader xmlTextReader = new XmlTextReader(fileStream);
            while (xmlTextReader.Read())
            {
                switch (xmlTextReader.Name)
                {
                    case "UrlAddress":
                        this.LastUpdateInfo.UpdateFileUrl = xmlTextReader.GetAttribute("URL");
                        break;
                    case "UpdateTime":
                        var b = xmlTextReader.GetAttribute("Date");
                        var a = Convert.ToDateTime(xmlTextReader.GetAttribute("Date"));
                        this.LastUpdateInfo.UpdateTime = Convert.ToDateTime(xmlTextReader.GetAttribute("Date"));
                        break;
                    case "Version":
                        this.LastUpdateInfo.Version = xmlTextReader.GetAttribute("VersionNum");
                        break;
                    default:
                        break;
                }
            }
            xmlTextReader.Close();
            fileStream.Close();
        }

        /// <summary>
        /// 从服务器下载最新的更新文件信息并封装到更新对象属性【版本，跟新时间，更新文件列表】
        /// </summary>
        private void GetNewUpdateInfo()
        {
            //下载最新的跟更新文件到临时目录
            string newXmlTempPath = TempFilePath+ "\\UpdateList.xml";
            WebClient webClient = new WebClient();
            Console.WriteLine("旧地址"+this.LastUpdateInfo.UpdateFileUrl + "\\UpdateList.xml");
            //webClient.DownloadFile(@"C:\Users\GJQ\Desktop\Test\UpdateList.xml", newXmlTempPath);
            webClient.DownloadFile(this.LastUpdateInfo.UpdateFileUrl + "\\UpdateList.xml", newXmlTempPath);
            //封装更新的信息
            FileStream fileStream = new FileStream(newXmlTempPath,FileMode.Open);
            XmlTextReader xmlTextReader = new XmlTextReader(fileStream);
            while (xmlTextReader.Read())
            {
                switch (xmlTextReader.Name)
                {
                    case "UrlAddress":
                        this.NowUpdateInfo.UpdateFileUrl = xmlTextReader.GetAttribute("URL");
                        break;
                    case "UpdateTime":
                        this.NowUpdateInfo.UpdateTime = Convert.ToDateTime(xmlTextReader.GetAttribute("Date"));
                        break;
                    case "Version":
                        this.NowUpdateInfo.Version = xmlTextReader.GetAttribute("VersionNum");
                        break;
                    case "UpdateFile":
                        string version = xmlTextReader.GetAttribute("Version");
                        string fileName = xmlTextReader.GetAttribute("FileName");
                        string contentLength = xmlTextReader.GetAttribute("ContentLength");
                        this.NowUpdateInfo.FileList.Add(new string[] { fileName, contentLength, version,"0%"});
                        break;
                    default:
                        break;
                }
            }
            xmlTextReader.Close();
            fileStream.Close();
        }
        //, int downLoadBytes, int fileLength
        //声明用于显示更新进度的委托[下载的文件顺序，已经下载的百分比]
        public delegate void ShowUpdateProcess(int fileIndex, int finishedPrecent);
        //创建委托对象【在更新床头在与之相关联】
        public ShowUpdateProcess showUpdateProcess;
        /// <summary>
        /// 根据跟新文件列表下载更新文件，并同步显示下载的百分比
        /// </summary>
        public void DownLoadFiles()
        {
            List<string[]> fileList = this.NowUpdateInfo.FileList;
            string updateFileUrl = this.NowUpdateInfo.UpdateFileUrl;
            //循环下载文件
            for (int i = 0; i < fileList.Count; i++)
            {
                //1.连接远程服务器中的指定文件，并准备读取
                string filename = this.NowUpdateInfo.FileList[i][0];//需要下载的文件名称
                //2.要下载的文件地址
                string fileUrl = updateFileUrl + "\\" + filename;
                Console.WriteLine("文件地址："+ fileUrl);
                //根据文件的url连接到服务器，创建请求对象
                WebRequest request = WebRequest.Create(fileUrl);
                //根据请求对象创建响应对象
                WebResponse response = request.GetResponse();
                //通过响应对象返回数据流对象
                Stream stream = response.GetResponseStream();
                //创建流读取器对象
                //StreamReader streamReader = new StreamReader(stream);
                //3.在线读取以及连接的远程文件，并基于委托反馈文件下载的进度
                long fileLength = response.ContentLength;//获取响应对象的数据长度
                byte[] bufferByte = new byte[fileLength];//根据当前文件的字节数创建字节数组
                int allByte = bufferByte.Length;//得到总字节数
                int readCount = 10000;
                int startByte = 0;//表示第一个字节
                while (fileLength > 0)
                {
                    Application.DoEvents();//表示在一个线程中同时处理其他事件（没有该语句，同步更新的百分比无法实现）
                    if (allByte - readCount < 0)
                    {
                        readCount = allByte;
                    }
                    int downLoadByte = stream.Read(bufferByte, startByte, readCount);//开始读取字节流
                    if (downLoadByte == 0) break;
                    startByte += downLoadByte;//累加已经下载的字节数
                    allByte -= downLoadByte;//为下载的字节数
                  
                    //计算完成的百分比
                    float part = (float)startByte / 1024;
                    float total = (float)bufferByte.Length/1024;
                    int precent = Convert.ToInt32((part/ total)*100);
                    //通过委托展示下载百分比
                    showUpdateProcess(i, precent);
                }
                //4.保存读取的文件
                string newFileName = this.TempFilePath + "\\" + filename;
                FileStream fs = new FileStream(newFileName, FileMode.OpenOrCreate, FileAccess.Write);//写入文件
                fs.Write(bufferByte,0, bufferByte.Length);
                stream.Close();
                fs.Close();
            }
          
        }
        /// <summary>
        /// 将下载的文件复制到应用程序目录
        /// </summary>
        /// <returns></returns>
        public bool CopyFile()
        {
            string[] files = Directory.GetFiles(TempFilePath);
            foreach (string name in files)
            {
                string currentFileName = Path.GetFileName(name);
                if (File.Exists(currentFileName))//如果在程序目录中则删掉
                {
                    File.Delete(currentFileName);
                }
                File.Copy(name, currentFileName);
            }
            return true;
        }
    }
}
